home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
src
/
haeberli
/
libgutil
/
stream.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
16KB
|
806 lines
/*
* Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
/*
* stream -
* Convert an image into lines.
*
* Paul Haeberli - 1987
*
*/
#include "image.h"
#include "obj.h"
#include "vect.h"
#include "stream.h"
static statecoords();
static nextstate();
static statesame();
static unmarkcorners();
static marksharpcorners();
static badpoop();
static lineintersect();
object *imgtoshape();
#define BOXTOL (1.5*stoutscale) /* for ratherstraight */
#define CORNER (1.2) /* for marksharpcorners */
#define SHORTSTEP (3.0*stepsize) /* for fixcorners */
#define SHARP (1.0)
#define NOTSHARP (0.0)
#define SKIPPOINTS (1)
#define XBRIDGE (1<<8)
#define YBRIDGE (2<<8)
#define getpixel(x,y) ((*(stfb+((y)*stxsize)+(x)))&0xff)
#define setpixel(x,y,val) *(stfb+((y)*stxsize)+(x)) = (val)
#define setpixelbit(x,y,val) *(stfb+((y)*stxsize)+(x)) |= (val)
#define clrpixelbit(x,y,val) *(stfb+((y)*stxsize)+(x)) &= ~(val)
#define getpixelbit(x,y,val) (*(stfb+((y)*stxsize)+(x)) & (val))
static short *stfb;
static int stxsize, stysize;
static float stoutscale;
object *fbtoshape();
static float toslope();
static float fakeatan();
static float pntdist();
static float pntslope();
static float slopedelta();
static point *pntinterp();
static object *followpath();
static object *resampleobj();
static object *smoothobj();
static object *filterobj();
static object *fixcorners();
#define DX 0 /* X axis */
#define DY 1 /* Y axis */
#define MINUS 0 /* minus direction */
#define PLUS 1 /* plus direction */
typedef struct pathstate {
int x; /* origin x and y */
int y;
int axis; /* DX or DY */
int direction; /* PLUS or MINUS */
} pathstate;
pathstate statetab[4][3] = {
{ { -1, 0, DY, MINUS }, /* from DX MINUS */
{ 0, -1, DX, MINUS },
{ 0, 0, DY, PLUS } },
{ { 0, 1, DY, PLUS }, /* from DX PLUS */
{ 0, 1, DX, PLUS },
{ -1, 1, DY, MINUS }, },
{ { 0, 0, DX, PLUS }, /* from DY MINUS */
{ -1, 0, DY, MINUS },
{ 0, -1, DX, MINUS }, },
{ { 1, -1, DX, MINUS }, /* from DY PLUS */
{ 1, 0, DY, PLUS },
{ 1, 0, DX, PLUS }, },
};
#define FIX_SHARP 1
#define FIX_ISLONG 2
#define FIX_DELETE 4
typedef struct workpnt {
float x, y;
float slope;
short flags;
} workpnt;
object *imgtoshape(name,thresh,func,stepsize)
char *name;
int thresh, func;
float stepsize;
{
int xsize, ysize;
short *fb;
object *obj;
sizeofimage(name,&xsize,&ysize);
fb = (short *)shortimagedata(name),
obj = fbtoshape(fb,xsize,ysize,thresh,func,stepsize);
free(fb);
return obj;
}
static zaphigh()
{
int x, y;
short *sptr;
int val;
sptr = stfb;
for(y=0; y<stysize; y++)
for(x=0; x<stxsize; x++)
*sptr++ &= 0xff;
val = getpixel(0,0);
for(x=0; x<stxsize; x++)
setpixel(x,0,val);
for(x=0; x<stxsize; x++)
setpixel(x,stysize-1,val);
for(y=0; y<stysize; y++)
setpixel(0,y,val);
for(y=0; y<stysize; y++)
setpixel(stxsize-1,y,val);
}
object *fbtoshape(myfb,xsize,ysize,thresh,func,stepsize)
short *myfb;
int xsize, ysize, thresh, func;
float stepsize;
{
int x, y;
stfb = myfb;
stxsize = xsize;
stysize = ysize;
if(stxsize>stysize)
stoutscale = 1.0/stxsize;
else
stoutscale = 1.0/stysize;
zaphigh();
for(y=1; y<stysize; y++) {
for(x=1; x<stxsize; x++) {
if(getpixel(x,y) < thresh) {
if(getpixel(x-1,y) >= thresh)
setpixelbit(x,y,XBRIDGE);
if(getpixel(x,y-1) >= thresh)
setpixelbit(x,y,YBRIDGE);
} else {
if(getpixel(x-1,y) < thresh)
setpixelbit(x,y,XBRIDGE);
if(getpixel(x,y-1) < thresh)
setpixelbit(x,y,YBRIDGE);
}
}
}
return followpath(thresh,func,stepsize);
}
static object *followpath(thresh,func,stepsize)
int thresh, func;
float stepsize;
{
int x, y;
pathstate initstate, curstate;
float xcoord, ycoord;
int count;
object *objlist = 0;
object *path = 0;
count = 0;
for(y=1; y<stysize; y++) {
for(x=1; x<stxsize; x++) {
if(getpixelbit(x,y,XBRIDGE)) {
if(getpixel(x,y) >= thresh) { /* XXX change */
initstate.x = x;
initstate.y = y;
initstate.axis = DX;
initstate.direction = MINUS;
} else {
initstate.x = x;
initstate.y = y;
initstate.axis = DX;
initstate.direction = PLUS;
}
curstate = initstate;
statecoords(&curstate,thresh,&xcoord,&ycoord);
path = newobj();
addpoint(path,newpnt(stoutscale*xcoord,stoutscale*ycoord,0.0));
while(1) {
nextstate(&curstate);
if(curstate.axis == DX)
clrpixelbit(curstate.x,curstate.y,XBRIDGE);
else
clrpixelbit(curstate.x,curstate.y,YBRIDGE);
if(++count == SKIPPOINTS) {
statecoords(&curstate,thresh,&xcoord,&ycoord);
addpoint(path,newpnt(stoutscale*xcoord,stoutscale*ycoord,0.0));
count =0;
}
if(statesame(&curstate,&initstate))
break;
}
clrpixelbit(initstate.x,initstate.y,XBRIDGE);
statecoords(&initstate,thresh,&xcoord,&ycoord);
path = filterobj(path,func,stepsize);
if(path) {
path->type = OBJ_LOOP;
if(!objlist)
objlist = path;
else
addobject(objlist,path);
}
}
}
}
return objlist;
}
static object *filterobj(obj,func,stepsize)
object *obj;
int func;
float stepsize;
{
object *path;
int n;
if(func&RESAMPLEFUNC) {
path = resampleobj(obj,stepsize);
freeobj(obj);
if(!path)
return 0;
obj = path;
}
n = unmarkcorners(obj);
if(n<3) {
freeobj(obj);
return 0;
}
marksharpcorners(obj);
if(func&SMOOTHFUNC) {
path = smoothobj(obj);
freeobj(obj);
obj = path;
}
if(func&FIXCORNERFUNC) {
path = fixcorners(obj,stepsize);
freeobj(obj);
obj = path;
}
n = unmarkcorners(obj);
if(n<3) {
freeobj(obj);
return 0;
}
return obj;
}
static statesame(s1,s2)
pathstate *s1, *s2;
{
if(s1->x != s2->x)
return 0;
if(s1->y != s2->y)
return 0;
if(s1->axis != s2->axis)
return 0;
if(s1->direction != s2->direction)
return 0;
return 1;
}
static nextstate(s)
pathstate *s;
{
pathstate *next;
int x, y;
int major, minor;
major = (s->axis<<1)+s->direction;
x = s->x;
y = s->y;
for(minor=0; minor<3; minor++) {
next = &statetab[major][minor];
if(next->axis == DX) {
if(getpixelbit(x+next->x,y+next->y,XBRIDGE)) {
s->x += next->x;
s->y += next->y;
s->direction = next->direction;
s->axis = next->axis;
return;
}
} else {
if(getpixelbit(x+next->x,y+next->y,YBRIDGE)) {
s->x += next->x;
s->y += next->y;
s->direction = next->direction;
s->axis = next->axis;
return;
}
}
}
badpoop(0);
}
static statecoords(s,thresh,rx,ry)
pathstate *s;
int thresh;
float *rx, *ry;
{
int pixval0, pixval1;
float p0, delta;
int x, y;
x = s->x;
y = s->y;
pixval0 = getpixel(x,y);
if(pixval0 < thresh) {
if(s->axis == DX) {
pixval1 = getpixel(x-1,y);
if(pixval1 >= thresh) {
*rx = x + 0.5+(pixval0-thresh)/((float)(pixval1-pixval0));
*ry = y + 0.5;
} else
badpoop(1);
} else {
pixval1 = getpixel(x,y-1);
if(pixval1 >= thresh) {
*rx = x + 0.5;
*ry = y + 0.5+(pixval0-thresh)/((float)(pixval1-pixval0));
} else
badpoop(1);
}
} else {
if(s->axis == DX) {
pixval1 = getpixel(x-1,y);
if(pixval1 < thresh) {
*rx = x + 0.5+(pixval0-thresh)/((float)(pixval1-pixval0));
*ry = y + 0.5;
} else
badpoop(1);
} else {
pixval1 = getpixel(x,y-1);
if(pixval1 < thresh) {
*rx = x + 0.5;
*ry = y + 0.5+(pixval0-thresh)/((float)(pixval1-pixval0));
} else
badpoop(1);
}
}
}
static badpoop(n)
int n;
{
fprintf(stderr,"imgtoshape: bad poop %d\n",n);
exit(1);
}
static object *fixcorners(obj,stepsize)
object *obj;
float stepsize;
{
workpnt *shape;
workpnt *p, *np, *start, *end;
point *pnt;
int val, i, j, m, npoints;
object *robj;
float delta, dx, dy, z;
vect p1, p2, p3, p4, pinter;
/* count the points */
npoints = 0;
pnt = obj->points;
while(pnt) {
npoints++;
pnt = pnt->next;
}
/* put them into a handy data structure */
shape = (workpnt *)mymalloc(npoints*sizeof(workpnt));
pnt = obj->points;
p = shape;
for(i=0; i<npoints; i++) {
p->x = pnt->x;
p->y = pnt->y;
p->flags = 0;
if(pnt->z == SHARP) {
p->flags |= FIX_SHARP;
}
pnt = pnt->next;
p++;
}
/* add length and slope info */
for(i=0; i<npoints; i++) {
p = shape+i;
np = shape+((i+1)%npoints);
dx = p->x - np->x;
dy = p->y - np->y;
if(sqrt(dx*dx+dy*dy) > SHORTSTEP) {
p->flags |= FIX_ISLONG;
}
p->slope = toslope(np->x-p->x,np->y-p->y);
}
/*
* find place where we have long edge, then some number of sharp points
* followed by another long edge.
*/
for(i=0; i<npoints; i++) {
p = shape+i;
if(p->flags & FIX_ISLONG) {
j = 0;
while(1) {
p = shape+((i+j+1)%npoints);
if((p->flags & FIX_SHARP) == 0)
break;
if(p->flags & FIX_ISLONG)
break;
j++;
}
if(j>0 && j<3 && (p->flags & FIX_SHARP) &&
(p->flags & FIX_ISLONG)) {
start = shape+((i+0)%npoints);
end = p;
delta = slopedelta(start->slope,end->slope);
if((delta>0.5) && (delta<3.5)) {
for(m=1; m<=j; m++) {
p = shape+((i+m+1)%npoints);
p->flags |= FIX_DELETE;
}
p = shape+((i+0)%npoints);
p1.x = p->x;
p1.y = p->y;
p = shape+((i+1)%npoints);
p2.x = p->x;
p2.y = p->y;
p = shape+((i+j+1)%npoints);
p3.x = p->x;
p3.y = p->y;
p = shape+((i+j+2)%npoints);
p4.x = p->x;
p4.y = p->y;
lineintersect(&p1,&p2,&p3,&p4,&pinter);
p = shape+((i+1)%npoints);
p->x = pinter.x;
p->y = pinter.y;
}
}
}
}
/* make a linked list object to return */
robj = newobj();
p = shape;
pnt = 0;
for(i=0; i<npoints; i++) {
if(!(p->flags & FIX_DELETE)) {
if(p->flags & FIX_SHARP)
z = SHARP;
else
z = NOTSHARP;
if(!pnt) {
robj->points = pnt = newpnt(p->x,p->y,z);
} else {
pnt->next = newpnt(p->x,p->y,z);
pnt = pnt->next;
}
}
p++;
}
free(shape);
return robj;
}
static point *ratherstraight(start,firstpoint,n)
point *start, *firstpoint;
int n;
{
int count;
point *end, *p;
float dist;
float a, b, c; /* for ax+by+c == 0 */
float mag;
end = start;
for(count=0; count<n; count++) {
if(end->next) {
end = end->next;
if(end->z == SHARP && (count != n-1))
return 0;
} else {
if(count == n-1)
end = firstpoint;
else
return 0;
}
}
if(n == 1)
return end;
else {
a = end->y - start->y;
b = end->x - start->x;
mag = sqrt(a*a+b*b);
a /= mag;
b /= mag;
c = (a*start->x)-(b*start->y);
p = start->next;
while(p!=end && p) {
dist = (a*p->x)-(b*p->y)-c;
if(dist<0.0)
dist = -dist;
if(dist>BOXTOL)
return 0;
p = p->next;
}
return end;
}
}
static object *smoothobj(obj)
object *obj;
{
object *cobj;
point *pnt, *cpnt, *lastpnt, *npnt;
float curslope;
int n;
cobj = newobj();
pnt = obj->points;
cpnt = newpnt(pnt->x,pnt->y,pnt->z);
cobj->points = cpnt;
while(pnt) {
n = 1;
lastpnt = 0;
while(1) {
if(npnt = ratherstraight(pnt,obj->points,n)) {
if(npnt==obj->points)
return cobj;
if(npnt->z == SHARP) {
cpnt->next = newpnt(npnt->x,npnt->y,npnt->z);
cpnt = cpnt->next;
pnt = npnt;
break;
}
lastpnt = npnt;
n++;
} else {
if(lastpnt) {
cpnt->next = newpnt(lastpnt->x,
lastpnt->y,lastpnt->z);
cpnt = cpnt->next;
pnt = lastpnt;
break;
} else {
pnt = pnt->next;
break;
}
}
}
}
return cobj;
}
static object *resampleobj(obj,stepsize)
object *obj;
float stepsize;
{
object *cobj;
point *pnt, *cpnt;
float curslope;
float totallength;
float step;
float need, used;
stepsize *= stoutscale;
cobj = NULL;
totallength = 0;
pnt = obj->points;
while(pnt) {
if(pnt->next)
pnt->z = pntdist(pnt,pnt->next);
else
pnt->z = pntdist(pnt,obj->points);
totallength += pnt->z;
pnt = pnt->next;
}
if(totallength>(2*stepsize)) {
step = totallength/(int)((totallength/stepsize));
cobj = newobj();
pnt = obj->points;
cpnt = newpnt(pnt->x,pnt->y,0.0);
cobj->points = cpnt;
need = step;
used = 0.0;
while(pnt) {
if(((1.0-used)*pnt->z) < need) {
need -= (1.0-used)*pnt->z;
used = 0.0;
pnt = pnt->next;
} else {
if(pnt->next)
cpnt->next=pntinterp(pnt,pnt->next,used+need/pnt->z);
else
cpnt->next=pntinterp(pnt,obj->points,used+need/pnt->z);
cpnt = cpnt->next;
used += need/pnt->z;
need = step;
}
}
if(pntdist(cobj->points,cpnt)<(step/10.0))
cobj->points = cobj->points->next;
}
return cobj;
}
static unmarkcorners(obj)
object *obj;
{
point *pnt;
int n;
pnt = obj->points;
n = 0;
while(pnt) {
pnt->z = NOTSHARP;
pnt = pnt->next;
n++;
}
return n;
}
static marksharpcorners(obj)
object *obj;
{
point *pnt, *npnt, *nnpnt, *nnnpnt;
float slope1, slope2;
pnt = obj->points;
if(pnt->next)
npnt = pnt->next;
else
npnt = obj->points;
if(npnt->next)
nnpnt = npnt->next;
else
nnpnt = obj->points;
if(nnpnt->next)
nnnpnt = nnpnt->next;
else
nnnpnt = obj->points;
while(pnt) {
if(slopedelta(pntslope(pnt,npnt),pntslope(nnpnt,nnnpnt))>CORNER) {
npnt->z = SHARP;
nnpnt->z = SHARP;
}
pnt = pnt->next;
npnt = nnpnt;
nnpnt = nnnpnt;
if(nnpnt->next)
nnnpnt = nnpnt->next;
else
nnnpnt = obj->points;
}
}
static float slopedelta(s1,s2)
float s1, s2;
{
float delta;
if(s1<s2)
delta = s2-s1;
else
delta = s1-s2;
if(delta>4.0)
delta = 8.0-delta;
return delta;
}
static float pntslope(p1,p2)
point *p1, *p2;
{
if(p2)
return toslope(p2->x-p1->x,p2->y-p1->y);
else
return 100.0;
}
static float pntdist(p1,p2)
point *p1, *p2;
{
float dx, dy;
dx = p1->x - p2->x;
dy = p1->y - p2->y;
return sqrt(dx*dx+dy*dy);
}
static point *pntinterp(p1,p2,param)
point *p1, *p2;
float param;
{
float x, y, z;
float a, b;
a = param;
b = 1.0-a;
x = b*p1->x + a*p2->x;
y = b*p1->y + a*p2->y;
return newpnt(x,y,0.0);
}
static float fakeatan(a,b)
float a, b;
{
a = a/b;
if(a>0.5773502)
return 0.666666 + (0.333333/(1.0-0.5773502))*(a-0.5773502);
else
return a*(0.666666/0.5773502);
}
static float toslope(dx,dy)
float dx, dy;
{
int flipx, flipy;
float val;
if(dx<0) {
flipx = 1;
dx = -dx;
} else
flipx = 0;
if(dy<0) {
flipy = 1;
dy = -dy;
} else
flipy = 0;
if(dy<dx)
val = fakeatan(dy,dx);
else
val = 2.0-fakeatan(dx,dy);
if(flipx)
val = 4.0-val;
if(flipy)
val = 8.0-val;
return val;
}
static lineintersect(p1,p2,p3,p4,pinter)
vect *p1, *p2, *p3, *p4, *pinter;
{
float dx, dy, mag;
float x3, y3;
float x4, y4;
float xc, yc;
float dx2, dy2;
dx = p2->x - p1->x;
dy = p2->y - p1->y;
mag = sqrt(dx*dx+dy*dy);
dx /= mag;
dy /= mag;
p3->x -= p1->x;
p3->y -= p1->y;
p4->x -= p1->x;
p4->y -= p1->y;
x3 = dx*p3->x + dy*p3->y;
y3 = dx*p3->y - dy*p3->x;
x4 = dx*p4->x + dy*p4->y;
y4 = dx*p4->y - dy*p4->x;
xc = x3 + ((y3*(x4-x3))/(y3-y4));
pinter->x = p1->x + dx*xc;
pinter->y = p1->y + dy*xc;
}